use std::error::Error;

use opencv::{
  boxed_ref::BoxedRef,
  core::{
    bitwise_and, count_non_zero, has_non_zero, in_range, min_max_loc, split, Mat, MatTraitConst,
    Point, Rect, Vector,
  },
  imgproc::{match_template_def, threshold, THRESH_BINARY, TM_CCOEFF_NORMED},
};

pub struct Cv {}

impl Cv {
  pub fn is_warn(img: &BoxedRef<'_, Mat>) -> Result<bool, Box<dyn Error>> {
    let mut mask = Mat::default();
    in_range(img, &[0, 0, 64], &[96, 96, 255], &mut mask)?;
    let mut output = Mat::default();
    bitwise_and(img, &img, &mut output, &mask)?;
    let mut channels = Vector::<Mat>::default();
    split(&output, &mut channels)?;
    let pixel = count_non_zero(&channels.get(2)?)?;
    // println!("{:?}", pixel);
    Ok(1.0 * (pixel as f32) / (img.rows() as f32 * img.cols() as f32) > 0.1)
  }

  pub fn is_match(img: &BoxedRef<Mat>, temp: &Mat) -> Result<(bool, Point), Box<dyn Error>> {
    // debug!(
    //   "{} {} {} {}",
    //   temp.rows(),
    //   temp.cols(),
    //   img.rows(),
    //   img.cols()
    // );
    let mut res = Mat::default();
    match_template_def(&img, &temp, &mut res, TM_CCOEFF_NORMED)?;
    let mut max_val = 0.0;
    let mut max_loc = Point::default();
    let mask = Mat::default();
    min_max_loc(
      &res,
      None,
      Some(&mut max_val),
      None,
      Some(&mut max_loc),
      &mask,
    )?;
    if max_val < 0.8 {
      return Ok((false, max_loc));
    }
    Ok((true, max_loc))
  }

  pub fn has_non_zero(
    img: &Mat,
    x: i32,
    y: i32,
    w: i32,
    h: i32,
    _threshold: f64,
  ) -> Result<bool, Box<dyn Error>> {
    let roi = img.roi(Rect::new(x, y, w, h))?;
    let mut thres = Mat::default();
    threshold(&roi, &mut thres, _threshold, 255.0, THRESH_BINARY)?;
    Ok(has_non_zero(&thres)?)
  }
}
